<?php
//券相关业务

namespace App\Services\Wx;

use App\Models\CouponReceiveModel;
use App\Models\PreCouponSendLogModel;
use App\Models\PreCouponSendTaskModel;
use App\Models\WxActionLogModel;
use App\Repositories\CouponReceiveRepository;
use App\Repositories\DinnerLogRepository;
use App\Services\ActivityService;
use App\Services\ReceiverService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;

class CouponService
{

    public function distribute($stockIds, $activityId)
    {
        $CouponReceiveRepo = new CouponReceiveRepository();
        // 在应发记录表中获取需要发送的受助人
        $receiverOpenids = PreCouponSendLogModel::where('activity_id', $activityId)
            ->whereDate('need_send_at', today())
            ->where('status', PreCouponSendLogModel::STATUS_NOT_SEND)
            ->pluck('openid', 'receiver_id');
        $wxCoupon = new WxCoupon();
        $nowDate = Carbon::now()->toDateTimeString();
        $receiverNum = count($receiverOpenids);
        $receiveIndex = 0;
        $receivedNum = 0; //已领取的券数
        $receivedData = [];
        $couponStatus = [];
        // 具体发送逻辑写这里(循环受助人)
        foreach ($receiverOpenids as $receiverId => $openid) {
            // 多张券同时使用
            foreach ($stockIds as $stockId) {
                // 如果所有人都发完，不管是否发放成功，都直接退出
                if ($receiveIndex == $receiverNum) {
                    break;
                }
                $stockInfo = $wxCoupon->getCouponStockDetail($stockId);
                if (isset($stockInfo['code'])) {
                    continue;
                }
                $stockReceiveNum = $stockInfo['stock_use_rule']['max_coupons'] - $stockInfo['distributed_coupons'];
                // 验证代金券信息
                $verRes = CouponService::verifyCouponStock($stockInfo, $nowDate);
                // 如果券验证不通过或已经领取完成，则使用下一张券
                if ($verRes['errcode'] != 0 || $stockReceiveNum == 0) {
                    continue;
                }
                //领取流程
                $outSuffix = sprintf("%04d", $receivedNum + 1);
                $outRequestNo = env('MCHID').date('YmdHis').$receiverId.$outSuffix; //本次发放 商户单据号，需保证唯一
                $receiveRes = $wxCoupon->receiveCoupon($openid, $stockId, $outRequestNo);
                if (isset($receiveRes['coupon_id'])) {
                    //领取成功，保存入库
                    $startDate = '';
                    $endDate = '';
                    if (!empty($stockInfo['available_begin_time'])) {
                        $startDate = Carbon::parse($stockInfo['available_begin_time'])->toDateTimeString();
                    }
                    if (!empty($stockInfo['available_end_time'])) {
                        $endDate = Carbon::parse($stockInfo['available_end_time'])->toDateTimeString();
                    }
                    // 如果数据有空值，则不存数据库
                    if (empty($receiveRes['coupon_id']) || empty($receiverId) || empty($activityId) || empty($stockId)
                        || empty($stockInfo['stock_use_rule']['fixed_normal_coupon']['coupon_amount']) || empty($outRequestNo)) {
                        continue;
                    }
                    $oneReceiveData = [
                        'activity_id'       => $activityId, //活动id
                        'coupon_id'         => $receiveRes['coupon_id'] ?? 0, //券id，由微信返回
                        'receiver_id'       => $receiverId, //领券人id
                        'openid'            => $openid, //领券人id
                        'stock_id'          => $stockId, //批次id
                        'coupon_name'       => $stockInfo['stock_name'] ?? '', //券名称
                        'receive_date'      => $nowDate, //领取时间
                        'coupon_amount'     => $stockInfo['stock_use_rule']['fixed_normal_coupon']['coupon_amount'] ?? 0,
                        //券金额，单位：分
                        'coupon_quota'      => $stockInfo['stock_use_rule']['fixed_normal_coupon']['transaction_minimum'] ?? 0,
                        //券限额，单位：分
                        'coupon_desc'       => $stockInfo['description'], //券描述
                        'coupon_start_date' => $startDate, //使用时间-开始
                        'coupon_end_date'   => $endDate, //使用时间-结束
                        'out_request_no'    => $outRequestNo, //本次发放 商户单据号，需保证唯一
                    ];
                    $receivedData[] = $oneReceiveData;
                    $receivedNum++; //领取成功，已领取数+1
                }
                // 检查卷数量，判断券使用情况
                $status = $this->checkCouponStatus($stockReceiveNum, $receiverNum, $verRes['errcode']);
                // 保存券状态
                if (!isset($couponStatus[$status])) {
                    $couponStatus[$status] = true;
                }
                $receiveIndex++;
            }
        }
        if (count($receivedData) > 0) {
            $saveRest = $CouponReceiveRepo->saveAllReceive($receivedData);
            if (!$saveRest) {
                // @todo 发送飞书信息

            }
        }
        // 修改记录表
        $this->updatePreCouponSendLogStatus($activityId);
    }

    /**
     * 根据任务处理发券
     * @param $taskId
     * @param $activityId
     * @return bool
     */
    public function distributeBySendTask($taskId, $activityId)
    {
        //获取活动信息
        $activityInfo = ActivityService::getRow([
            'id' => $activityId,
        ]);
        if (!$activityInfo || !$activityInfo['stock_ids']) {
            return false;
        }
        $stockIds = explode(',', $activityInfo['stock_ids']);
        //修改本次任务状态为发送中
        PreCouponSendTaskModel::query()
            ->where('id', $taskId)
            ->update([
                'status' => PreCouponSendTaskModel::STATUS_SENDING,
            ]);
        // 在应发记录表中获取需要发送的受助人
        $lastId = 0;
        $pageSize = 1000;
        while (true) {
            $sendLogList = PreCouponSendLogModel::query()
                ->lock() //主库查询
                ->where('task_id', $taskId)
                ->whereIn('status', [1, 3]) //失败的会再次重试
                ->where('id', '>', $lastId)
                ->select(['id', 'openid', 'receiver_id', 'activity_id'])
                ->limit($pageSize)
                ->orderBy('id', 'asc')
                ->get()
                ->toArray();
            if (!$sendLogList) {
                break;
            }
            foreach ($sendLogList as $val) {
                $disRes = $this->distributeCoupon($activityId, $stockIds, $val['receiver_id'], $val['openid'],
                    $val['id']);
                $logStatus = $disRes['errcode'] ? 3 : 2;
                $logStockId = $disRes['data']['stock_id'] ?? 0;
                //修改记录状态
                PreCouponSendLogModel::query()
                    ->where('id', $val['id'])
                    ->update([
                        'status'   => $logStatus,
                        'stock_id' => $logStockId,
                    ]);
                //失败推送飞书警报
                if($logStatus == 3) {
                    $this->feishuWarring("发券失败，log_id:{$val['id']}，请及时查看！");
                }
            }
        }
        //修改本次任务的状态为完成
        PreCouponSendTaskModel::query()
            ->where('id', $taskId)
            ->update([
                'status' => PreCouponSendTaskModel::STATUS_SENDED,
            ]);

        return true;
    }

    /**
     * 对用户发券
     * @param $activityId
     * @param $stockIds
     * @param $receiverId
     * @param $openid
     * @param $logId
     * @return array
     */
    public function distributeCoupon($activityId, $stockIds, $receiverId, $openid, $logId)
    {
        $nowDate = Carbon::now()->toDateTimeString();
        $wxCoupon = new WxCoupon();
        $couponReceiveRepo = new CouponReceiveRepository();
        //循环批次，使用余额的券
        foreach ($stockIds as $stockId) {
            //券批次信息
            $stockInfo = $wxCoupon->getCouponStockDetail($stockId);
            if (isset($stockInfo['code'])) {
                continue;
            }
            $stockReceiveNum = $stockInfo['stock_use_rule']['max_coupons'] - $stockInfo['distributed_coupons'];
            // 验证代金券信息
            $verRes = CouponService::verifyCouponStock($stockInfo, $nowDate);
            // 如果券验证不通过或已经领取完成，则使用下一张券
            if ($verRes['errcode'] != 0 || $stockReceiveNum == 0) {
                continue;
            }
            //领取流程
            $outRequestNo = env('MCHID').date('YmdHis').$receiverId.$logId; //本次发放 商户单据号，需保证唯一
            $receiveRes = $wxCoupon->receiveCoupon($openid, $stockId, $outRequestNo);
            if (!empty($receiveRes['code']) || empty($receiveRes['coupon_id'])) {
                //领取失败
                return resultError([
                    'stock_id' => $stockId,
                ]);
            }
            //领取成功，保存入库
            $startDate = '';
            $endDate = '';
            if (!empty($stockInfo['available_begin_time'])) {
                $startDate = Carbon::parse($stockInfo['available_begin_time'])->toDateTimeString();
            }
            if (!empty($stockInfo['available_end_time'])) {
                $endDate = Carbon::parse($stockInfo['available_end_time'])->toDateTimeString();
            }
            // 如果数据有空值，则不存数据库
            if (empty($receiveRes['coupon_id']) || empty($receiverId) || empty($activityId) || empty($stockId)
                || empty($stockInfo['stock_use_rule']['fixed_normal_coupon']['coupon_amount']) || empty($outRequestNo)) {
                continue;
            }
            $oneReceiveData = [
                'activity_id'       => $activityId, //活动id
                'coupon_id'         => $receiveRes['coupon_id'] ?? 0, //券id，由微信返回
                'receiver_id'       => $receiverId, //领券人id
                'openid'            => $openid, //领券人id
                'stock_id'          => $stockId, //批次id
                'coupon_name'       => $stockInfo['stock_name'] ?? '', //券名称
                'receive_date'      => $nowDate, //领取时间
                'coupon_amount'     => $stockInfo['stock_use_rule']['fixed_normal_coupon']['coupon_amount'] ?? 0,
                //券金额，单位：分
                'coupon_quota'      => $stockInfo['stock_use_rule']['fixed_normal_coupon']['transaction_minimum'] ?? 0,
                //券限额，单位：分
                'coupon_desc'       => $stockInfo['description'], //券描述
                'coupon_start_date' => $startDate, //使用时间-开始
                'coupon_end_date'   => $endDate, //使用时间-结束
                'out_request_no'    => $outRequestNo, //本次发放 商户单据号，需保证唯一
            ];
            $couponReceiveRepo->save($oneReceiveData);
            break;
        }

        return resultSuccess('', [
            'stock_id' => $stockId,
        ]);
    }


    private function updatePreCouponSendLogStatus($activityId)
    {
        // 获取当天领取券的受助者的id
        $receiverIds = CouponReceiveModel::where('activity_id', $activityId)
            ->whereDate('receive_date', today())
            ->pluck('receiver_id')
            ->toArray();
        $receiverIds = array_unique($receiverIds);
        PreCouponSendLogModel::where('activity_id', $activityId)
            ->whereIn('receiver_id', $receiverIds)
            ->whereDate('need_send_at', today())
            ->update(
                ['status' => PreCouponSendLogModel::STATUS_SENDED]
            );
    }

    /**
     * 检查卷数量，判断券使用情况
     * @param $stockInfo
     * @param $receiverOpenids
     */
    private function checkCouponStatus($stockReceiveNum, $receiverNum, $errcode)
    {
        $status = 0;
        if ($receiverNum > $stockReceiveNum) {                                  // 领券人数大于卷的数量
            $status = 1;
        } else {
            if (floor($stockReceiveNum / $receiverNum) <= 3) {          // 卷数量发送天数小于3天
                $status = 2;
            } else {
                if ($errcode != 0) {                                                  // 券验证失败
                    $status = 3;
                }
            }
        }
        return $status;
    }

    public function checkexist()
    {

    }

    /**
     * 获取用户券列表
     * @param $wxUserId
     * @return array
     */
    public static function getUserCouponList($wxUserId, $page = 1, $pageSize = 20, $type = 'all')
    {
        //获取用户
        $userInfo = UserService::getUsreInfoById($wxUserId);
        if (!$userInfo || !$userInfo->openid) {
            return resultError('用户信息有误');
        }
        $openid = $userInfo->openid;
        //获取用户领取的券列表
        $couponList = \App\Services\CouponService::getPageList([
            'openid' => $openid,
        ], $page, $pageSize, 1);
        $allList = self::userCouponListFormat($couponList['list']);
        $total = $couponList['total'];
        $output = [];
        if ($type == 'all' || $type == 'enabled') {
            $output['enabled'] = self::getEnabledCoupon($allList);
            unset($allList[0]);
            $total--;
        }
        if ($type == 'all' || $type == 'disabled') {
            $output['disabled'] = [
                'total'     => $total,
                'totalPage' => $couponList['totalPage'],
                'pageSize'  => $couponList['pageSize'],
                'list'      => $allList,
            ];
        }

        return resultSuccess($output);
    }

    public static function getEnabledCoupon($allList)
    {
        $coupon = [

        ];
        // 获取第一个
        if (count($allList) > 0 && $allList[0]['receive_date'] == date('Y-m-d')) {
            $coupon['coupon_name'] = $allList[0]['coupon_name'];
            $coupon['id'] = $allList[0]['id'];
            $coupon['coupon_amount_format_info'] = $allList[0]['coupon_amount_format_info'];
            $coupon['expired_at'] = date('Y-m-d');
        }
        return $coupon;
    }

    /**
     * 用户券列表格式化
     * @param $list
     * @return array
     */
    public static function userCouponListFormat($list = [])
    {
        $output = [];
        if (!$list) {
            return $output;
        }
        $unit = '元';
        $nowDate = Carbon::now()->toDateTimeString();
        foreach ($list as $val) {
            $val['coupon_start_date'] = Carbon::parse($val['coupon_start_date'])->toDateString();
            $val['coupon_end_date'] = Carbon::parse($val['coupon_end_date'])->toDateString();
            $item = [
                'id'                        => $val['id'], //数据主键，仅用于唯一性
                'coupon_id'                 => $val['coupon_id'], //券id，微信返回
                'coupon_name'               => $val['coupon_name'], //券名称
                'consume_status'            => $val['consume_status'], //核销状态：1.未核销；2.已核销；
                'coupon_amount'             => $val['coupon_amount'], //券金额，单位分
                'coupon_amount_format_info' => [
                    'amount' => $val['coupon_amount'] / 100,
                    'unit'   => $unit,
                ],
                'coupon_quota'              => $val['coupon_quota'], //券限额，单元元
                'coupon_quota_text'         => "", //券限额文案
                'use_data_text'             => date('Y年m月d日', strtotime(substr($val['receive_date'], 0, 10))),
                //券使用时间文案
                'coupon_desc'               => $val['coupon_desc'], //券描述
                'coupon_status'             => 1, //券状态：1.正常；2.已使用；3.已过期；
                'receive_date'              => substr($val['receive_date'], 0, 10),
            ];
            $couponQuotaFormat = $val['coupon_quota'] / 100;
            $item['coupon_quota_text'] = "超{$couponQuotaFormat}{$unit}可用";
            //券状态
            if ($val['consume_status'] == 2) {
                $item['coupon_status'] = 2;
            } elseif ($val['coupon_end_date'] < $nowDate) {
                $item['coupon_status'] = 3;
            }

            $output[] = $item;
        }

        return $output;
    }

    /**
     * 获取券详情
     * @param $couponId
     * @return array
     */
    public static function getCouponDetail($couponId)
    {
        $couponInfo = \App\Services\CouponService::getRow([
            'id' => $couponId,
        ]);
        if (!$couponInfo || !$couponInfo['activity_id']) {
            return resultError('数据有误');
        }
        //获取活动信息
        $activityInfo = ActivityService::getRow([
            'id' => $couponInfo['activity_id'],
        ]);
        if (!$activityInfo) {
            return resultError('活动信息有误');
        }
        $output = [
            'coupon_desc' => $activityInfo['coupon_desc'] ?? '',
        ];

        return resultSuccess($output);
    }


    /**
     * 验证券批次信息
     * @param $stockInfo
     * @param $nowDate
     * @param $logQuery
     * @return array|true
     */
    public static function verifyCouponStock($stockInfo = [], $nowDate = '', $logQuery = [])
    {
        if (!$stockInfo || !empty($stockInfo['code'])) { //批次查询有误
            //            self::saveReceiveLog($logQuery, $stockInfo, '获取券批次信息失败', 2);

            return resultError('批次查询有误');
        }
        $startDate = Carbon::parse($stockInfo['available_begin_time'])->toDateTimeString();
        $endDate = Carbon::parse($stockInfo['available_end_time'])->toDateTimeString();
        //            dd($startDate, $stockInfo['available_begin_time']);
        if ($nowDate < $startDate || $nowDate > $endDate) { //券批次过期
            //            self::saveReceiveLog($logQuery, $stockInfo, '券批次过期', 2);

            return resultError('券批次过期');
        }
        if ($stockInfo['status'] != 'running') { //券批次状态有误
            //            self::saveReceiveLog($logQuery, $stockInfo, '券批次状态有误', 2);

            return resultError('券批次状态有误');
        }
        //该批次已领完，领取下一批次
        if ($stockInfo['distributed_coupons'] >= $stockInfo['stock_use_rule']['max_coupons']) {
            //            self::saveReceiveLog($logQuery, $stockInfo, '券批次状态有误', 2);

            return resultError('该批次已领完');
        }

        return resultSuccess([], '验证通过');
    }

    /**
     * 用户核销消费券回调接收
     * @param $msg
     * @return array
     */
    public static function userConsumeCallback($msg = [])
    {
        $app = (new WxCoupon())->getApp();
        $serve = $app->getServer();
        $message = [];
        try {
            $message = $serve->getRequestMessage()->toArray();
        } catch (\Exception $e) {
        }
        $couponId = $message['coupon_id'] ?? 0;
        //回调信息日志
        \DB::table('wx_coupon_callback_msg_log')
            ->insert([
                'request_message' => json_encode($msg),
                'message'         => json_encode($message),
                'coupon_id'       => $couponId,
            ]);
        $acLogStatus = 2;
        // 插入用餐记录表
        $dinnerLog = new DinnerLogRepository();
        //        Log::info('CallBackLog:' . json_encode($msg));
        $dinnerLogRes = $dinnerLog->addByCoupon($couponId, $message['stock_creator_mchid']);
        //更新核销状态
        if ($message && $couponId) {
            $consumeRes = \App\Services\CouponService::updateConsumeStatusByCouponId($couponId, 2);
            if (!$consumeRes['errcode']) {
                $acLogStatus = 1;
            }
        }
        //记录微信动作日志
        $acLogDesc = ($acLogStatus == 1 && $dinnerLogRes) ? '成功' : '失败';
        $saveData = [
            'event'    => 'coupon_consume',
            'event_id' => $couponId,
            'desc'     => "用户核销消费券，{$acLogDesc}",
            'data'     => json_encode([
                'request_message' => $msg,
                'message'         => $message,
            ]),
            'status'   => $acLogStatus,
        ];
        WxActionLogModel::query()
            ->insert($saveData);

        if ($acLogStatus == 2) {
            return resultError('核销失败');
        }

        return resultSuccess([], '核销成功');
    }

    /**
     * 飞书警报推送
     * @param $taskId
     * @return true
     */
    public function feishuWarring($text) {
        $url = 'https://open.feishu.cn/open-apis/bot/v2/hook/b098fb81-1d00-4dd1-afc5-a03e99492abe';

        $res = \App\Services\Common\HttpClientEasy::clientPost($url, [], [
            'msg_type' => 'text',
            'content' => [
                'text' => "社会救助-助老餐，{$text}",
            ]
        ]);

        return true;
    }

}
